package com.franklin.java.completion.map;

import com.franklin.java.api.psi.data.PsiCallingElement;
import com.franklin.plugins.common.collection.PrettyArrayList;
import com.franklin.plugins.common.collection.PrettyHashSet;
import com.franklin.java.api.psi.data.PsiData;
import com.franklin.java.api.psi.data.resolver.PsiDataResolverFactory;
import com.franklin.java.completion.AbstractJavaDataCompletionContributor;
import com.franklin.java.completion.CompletionOrder;
import com.franklin.java.completion.map.mybatis.MapperMethodHandler;
import com.franklin.java.completion.map.mybatis.MybatisPlusMethodHandler;
import com.franklin.java.utils.CompletionUtils;
import com.franklin.java.utils.PsiUtils;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.franklin.plugins.common.utils.PatternUtils;
import com.franklin.plugins.common.utils.StringHelper;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.lookup.LookupElement;

import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiClass;
import com.intellij.psi.util.PsiTreeUtil;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

import static com.flychord.jdk.util.tuple.TupleAble.tuple;

/**
 * Map 数据代码提示
 *
 * @author Franklin
 * @since 2021/8/5 15:50
 */
public class MapDataCompletionContributor extends AbstractJavaDataCompletionContributor {

    public final static String MAP_TYPE_PATTERN
        = "^\\s*(?<fullStatement>(((IPage|List)\\s*<)?\\w*Map\\w*\\s*(<\\s*(?<keyType>\\w*)\\s*\\,\\s*(?<valueType>\\w*)\\s*>\\s*>?\\s*)?)\\s*%s\\s*=\\s*(?<declareStatement>[^;]*)\\s*);?[\\s\\S]*$";

    public final static String MAP_TYPE_PATTERN_NO_DECLARE
        = "^\\s*(?<fullStatement>(\\w*Map\\w*\\s*(<\\s*(?<keyType>\\w*)\\s*\\,\\s*(?<valueType>\\w*)\\s*>)?)\\s*%s\\s*);?[\\s\\S]*$";

    public final static String MAP_PUT_PATTERN
        = "^[\\s\\S]*\\s*%s\\s*\\.\\s*(put|putIfAbsent)\\s*\\(\\s*(?<key>.+)\\s*\\,\\s*(?<value>.+)\\s*\\)[\\s\\S]*$";

    public final static String MAP_PUT_KEY_PATTERN
        = "^[\\s\\S]*\\s*%s\\s*\\.\\s*(put|putIfAbsent)\\s*\\(\\s*%s\\s*\\,\\s*(?<value>.+)\\s*\\)[\\s\\S]*$";

    public final static String MAP_PUT_ALL_PATTERN
        = "^[\\s\\S]*\\s*%s\\s*\\.\\s*putAll\\s*\\(\\s*(?<originMap>.+)\\s*\\)[\\s\\S]*$";

    public final static String VARIABLE_PATTERN
        = "^\\S*\\s*(private|protected|public)?\\s*[(final\\s*static)|(static\\s*final)]?\\s*(?<variableType>\\w*)\\s*%s\\s*(=\\s*(?<variableValue>.+))*\\s*;[\\s\\S]*$";

    public final static String RETURN_VARIABLE_PATTERN
        = "^\\s*return\\s*(?<variable>.+)\\s*;[\\s\\S]*$";

    public final static String METHOD_NAME_PATTERN
        = "^\\s*(?<methodName>\\w*)\\s*(?<parameters>\\([\\s\\S]*\\))?$";

    public final static String METHOD_CALLER_PATTERN
        = "^\\s*(this)?\\s*\\.?\\s*(?<methodCaller>\\w*)\\s*\\.[\\s\\S]*$";

    public final static String EXPRESSION_CHAIN_PATTERN
        = "^\\s*(this)?\\s*\\.?\\s*(?<expressionChain>[\\s\\S]*)$";

    public final static String MAP_FROM_MAP_PATTERN_BASE
        = "^\\s*(\\(((IPage|List)\\s*<)?\\s*Map\\s*<\\s*%s\\s*,\\s*%s\\s*>\\s*>?\\s*\\))?\\s*(?<parentMap>\\w*)\\s*\\.\\s*(get|getOrDefault)\\s*\\(\\s*(?<key>\\S*)\\s*(\\,\\s*.*\\s*)?\\)[\\s\\S]*$";

    public final static String LIST_MAP_GET_PATTERN_BASE
        = "^[^=]*%s=(?<parentList>[^\\.]*)\\.get\\(\\d*\\)[\\s\\S]*$";

    private static final ThreadLocal<MapDeclaration> basicDeclaration = new ThreadLocal<>();

    private static final ThreadLocal<Integer> basicDepth = new ThreadLocal<>();

    private static final ThreadLocal<List<MapDeclaration>> basicDeclarations = new ThreadLocal<>();

    private static final MapDataCompletionContributor INSTANCE = new MapDataCompletionContributor();

    public static MapDataCompletionContributor getInstance() {
        return INSTANCE;
    }

    @Override
    public boolean isCorrectCompletionParameter(@NotNull CompletionParameters parameters) {
        String psiType = callingElementType.get();
        if (DataCheckUtils.isEmpty(psiType)){
            return false;
        }

//        String typeText = PsiUtils.resolveTypeNameFromGetClassMethodResult(psiType);
        return PsiUtils.isJavaMap(parameters.getOriginalFile().getProject(), psiType);
    }

    @Override
    public List<LookupElement> getLookupElements(CompletionParameters parameters) {
        List<LookupElement> result = new ArrayList<>();

        PsiCallingElement psiCallingElement = callingElementCache.get();
        PsiElement callingElement = psiCallingElement.getCallingElement();
        try {
            PsiCodeBlock psiCodeBlock = PsiTreeUtil.getParentOfType(callingElement, PsiCodeBlock.class);
            List<MapData> mapDataList = new PrettyArrayList<>();
            MapDeclaration mapDeclaration = findMapDeclaration(true, psiCodeBlock, callingElement);

            basicDeclaration.set(mapDeclaration);
            List<MapDeclaration> mapDeclarations = new PrettyArrayList<>();

            MapDeclaration temp = mapDeclaration;
            int dep = 0;

            while (Objects.nonNull(temp) && Objects.nonNull(temp.getChild())) {
                mapDeclarations.add(temp);
                temp = temp.getChild();
                dep++;
            }
            if (Objects.nonNull(temp)) {
                mapDeclarations.add(temp);
            }
            basicDepth.set(mapDeclarations.size());
            basicDeclarations.set(mapDeclarations);
            findMapData(1, psiCodeBlock, callingElement, mapDeclaration, mapDataList);
//            if (temp == mapDeclaration){
//            }else {
//                findMapData(1,psiCodeBlock, callingElement, mapDeclaration, mapDataList);
//            }

            
            mapDataList.forEach(mapData -> {
                String key = mapData.getKey().getValue();
                String type = mapData.getValue().getType();
                LookupElement lookElement = CompletionUtils.getLookElement(
                    key,
                    type,
                    "  " + mapData.getDescription(),
                    new MapDataInsertHandler(mapData,psiCallingElement)
                );
                result.add(lookElement);
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            basicDeclaration.remove();
            basicDepth.remove();
            basicDeclarations.remove();
        }
        return result;
    }

    @Override
    public CompletionOrder getOrder() {
        return CompletionOrder.HIGH;
    }

    private void findMapData(
        int depth,
        PsiCodeBlock psiCodeBlock,
        PsiElement mapElement,
        MapDeclaration mapDeclaration,
        List<MapData> mapDataList
    ) throws Exception {
        if (Objects.isNull(mapDeclaration)) {
            mapDeclaration = findMapDeclaration(true, psiCodeBlock, mapElement);
        }
        if (Objects.isNull(mapDeclaration)) {
            return;
        }
        if (!mapDeclaration.isMethodCall()) {
            //是底层new，直接添加当前代码块的所有put以及putAll结果
            findPutMethod(depth, psiCodeBlock, mapElement, mapDataList);
            PsiElement psiDeclarationStatement = mapDeclaration.getDeclareStatement();
            String declarationStatementText = psiDeclarationStatement.getText();
            String declareLast = mapDeclaration.getDeclareLast();
            if (DataCheckUtils.isNotEmpty(declareLast)) {
                PsiElement declareElement = psiDeclarationStatement.findElementAt(
                    declarationStatementText.indexOf(declareLast) + declareLast.length() / 2
                );
                PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(declareElement, PsiMethodCallExpression.class);
                if (Objects.nonNull(methodCallExpression)) {
                    MapDeclaration mapDeclarationInMethodChain = findMapDeclarationInMethodChain(methodCallExpression);
                    if (Objects.nonNull(mapDeclarationInMethodChain)) {
                        findPutMethodInMethod(depth, mapDeclarationInMethodChain, mapElement, mapDataList);
                    }
                }
            }
        } else {
            //方法返回结果，进到方法里寻找
            findPutMethodInMethod(depth, mapDeclaration, mapElement, mapDataList);
        }
        if (mapDeclaration.isCollection()) {
            findPutMethod(depth, psiCodeBlock, mapElement, mapDataList);
        }
        if (mapDeclaration.isClassField()) {
            PsiClass psiClass = PsiTreeUtil.getParentOfType(mapElement, PsiClass.class);
            if (Objects.nonNull(psiClass)) {
                PsiClassInitializer[] initializers = psiClass.getInitializers();
                for (PsiClassInitializer initializer : initializers) {
                    findMapData(depth, initializer.getBody(), mapElement, mapDeclaration, mapDataList);
                }
            }
        }
    }

    private void findPutMethodInMethod(
        int depth, @NotNull MapDeclaration mapDeclaration,
        @NotNull PsiElement callingElement,
        List<MapData> mapDataList
    ) throws Exception {
        Project project = callingElement.getProject();
        PsiElement declareStatement = mapDeclaration.getDeclareStatement();
        String declareStatementText = declareStatement.getText();
        String declareLast = mapDeclaration.getDeclareLast();
        PsiMethodCallExpression psiMethodCallExpression = (PsiMethodCallExpression) declareStatement;
        MybatisPlusMethodHandler mybatisPlusMethodHandler = new MybatisPlusMethodHandler(project);
        List<MapData> mapDataListFromQueryWrapper = mybatisPlusMethodHandler.resolveMethod(psiMethodCallExpression);
        if (DataCheckUtils.isNotEmpty(mapDataListFromQueryWrapper)) {
            mapDataList.addAll(mapDataListFromQueryWrapper);
        }

        PsiReferenceExpression psiReferenceExpression = psiMethodCallExpression.getMethodExpression();
        PsiMethod psiMethod = psiMethodCallExpression.resolveMethod();
        if (Objects.isNull(psiMethod)) {
            return;
        }
        PsiCodeBlock psiMethodBody = psiMethod.getBody();
        String parameterText = psiMethod.getParameterList().getText();
        if (Objects.isNull(psiMethodBody)) {
            //Mybatis Mapper方法
            MapperMethodHandler mapperMethodHandler = new MapperMethodHandler(project);
            List<MapData> mapDataFromMapperSql = mapperMethodHandler.resolveMethod(psiMethod);
            if (DataCheckUtils.isEmpty(mapDataFromMapperSql)) {
                //接口，尝试寻找实现类
                String expressionChainText = psiReferenceExpression.getText();
                Map<String, String> methodCallerResult = PatternUtils.find(METHOD_CALLER_PATTERN, expressionChainText, "methodCaller");
                String methodCaller = methodCallerResult.getOrDefault("methodCaller", "");
                if (DataCheckUtils.isEmpty(methodCaller)) {
                    return;
                }
                PsiElement methodCallElement = psiMethodCallExpression.findElementAt(expressionChainText.indexOf(methodCaller));
                PsiMethod methodImplementation = findAbstractMethodImplementation(methodCallElement, expressionChainText, parameterText, mapDeclaration);
                if (Objects.nonNull(methodImplementation)) {
                    psiMethodBody = methodImplementation.getBody();
                }
            }else {
                mapDataList.addAll(mapDataFromMapperSql);
            }
        }
        if (Objects.isNull(psiMethodBody)) {
            return;
        }

        //先找到返回值的变量
        String psiMethodBodyText = psiMethodBody.getText();
        String[] lines = psiMethodBodyText.split("\n");
        int length = 0;
        for (String line : lines) {
            if (line.trim().startsWith("/")) {
                length = length + line.length() + 1;
                continue;
            }
            Map<String, String> findResult = PatternUtils.find(RETURN_VARIABLE_PATTERN, line, "variable");
            String variable = findResult.getOrDefault("variable", "");
            if (DataCheckUtils.isNotEmpty(variable)) {
                PsiElement returnElement = psiMethodBody.findElementAt(length + line.indexOf(variable));
//                PsiMethodCallExpression fullMethodCallExpression = PsiUtils.findFullMethodCallExpression(returnElement, variable);
//                if (Objects.nonNull(fullMethodCallExpression)){
//                    returnElement = fullMethodCallExpression;
//                }
                PsiCodeBlock psiCodeBlock = PsiTreeUtil.getParentOfType(returnElement, PsiCodeBlock.class);
                findMapData(depth, psiCodeBlock, returnElement, null, mapDataList);
            }

            length = length + line.length() + 1;
        }
    }

    /**
     * 寻找抽象方法的实例方法<br>
     *
     * @return void
     * @author Franklin
     * @since 2021/8/12 11:45
     */
    private PsiMethod findAbstractMethodImplementation(
        @NotNull PsiElement methodCallElement,
        @NotNull String expressionChainText,
        String parameterText,
        @NotNull MapDeclaration mapDeclaration) {
        //去除this关键字
        Map<String, String> expressionChainResult = PatternUtils.find(EXPRESSION_CHAIN_PATTERN, expressionChainText, "expressionChain");
        String expressionChain = expressionChainResult.getOrDefault("expressionChain", "");
        if (DataCheckUtils.isEmpty(expressionChain)) {
            return null;
        }

        String[] chains = expressionChain.split("\\.");
        if (ArrayUtils.isEmpty(chains) || chains.length < 2) {
            return null;
        }


        String methodCallElementText = methodCallElement.getText();
        PsiData data = PsiDataResolverFactory.resolveData(methodCallElementText, methodCallElementText, methodCallElement);
        PsiElement declareElement = data.getPsiElement();

        String callMethodName = chains[1];

        Map<String, String> methodNameResult = PatternUtils.find(METHOD_NAME_PATTERN, callMethodName, "methodName", "parameters");
        callMethodName = methodNameResult.getOrDefault("methodName", callMethodName);
        String parameters = methodNameResult.getOrDefault("parameters", "()");

        //寻找实现类
        PsiClass implementClass = PsiUtils.findAbstractMethodImplementClass(data, callMethodName);
        if (Objects.isNull(implementClass)) {
            return null;
        }

        PsiMethod[] psiMethods = implementClass.getMethods();
        PsiMethod targetMethod = null;
        parameterText = StringUtils.deleteWhitespace(parameterText);
        for (PsiMethod psiMethod : psiMethods) {
            String psiMethodName = psiMethod.getName();
            String paramsText = psiMethod.getParameterList().getText();
            if (StringHelper.equals(psiMethodName, callMethodName)) {
                paramsText = StringUtils.deleteWhitespace(paramsText);
                if (StringHelper.equals(paramsText, parameterText)) {
                    targetMethod = psiMethod;
                    break;
                }
            }
        }

        if (Objects.isNull(targetMethod)) {
            return null;
        }

        if (chains[chains.length - 1].equals(targetMethod.getName())) {
            return targetMethod;
        }

        //递归继续寻找最终提供Map的方法
        PsiCodeBlock targetMethodBody = targetMethod.getBody();
        if (Objects.isNull(targetMethodBody)) {
            return null;
        }

        PsiElement newMethodCallElement = null;
        String psiMethodBodyText = targetMethodBody.getText();
        String[] lines = psiMethodBodyText.split("\n");
        int length = 0;
        for (String line : lines) {
            if (line.trim().startsWith("/")) {
                length = length + line.length() + 1;
                continue;
            }
            Map<String, String> findResult = PatternUtils.find(RETURN_VARIABLE_PATTERN, line, "variable");
            String variable = findResult.getOrDefault("variable", "");
            if (DataCheckUtils.isNotEmpty(variable)) {
                PsiElement returnElement = targetMethodBody.findElementAt(length + line.indexOf(variable));
                if (PsiUtils.isKeyWord_New(returnElement)) {
                    //声明新对象
                    String implementType = PatternUtils.findImplementType(variable);
                    if (DataCheckUtils.isNotEmpty(implementType)) {
                        returnElement = targetMethodBody.findElementAt(length + line.indexOf(implementType));
                    }
                } else {
                    //没有声明新对象
                    //do nothing
                }
                newMethodCallElement = returnElement;
                break;
            }

            length = length + line.length() + 1;
        }

        if (Objects.isNull(newMethodCallElement)) {
            return null;
        }

        //重新封装链式表达式
        //将chains[]的前两个过滤
        //调用对象替换为newMethodCallElement
        List<String> newChainList = new PrettyArrayList<>();
        newChainList.add(newMethodCallElement.getText());
        for (int i = 2; i < chains.length; i++) {
            newChainList.add(chains[i]);
        }
        String newExpressionChainText = String.join(".", newChainList);

        return findAbstractMethodImplementation(newMethodCallElement, newExpressionChainText, parameterText, mapDeclaration);
    }


    /**
     * 寻找声明变量语句<br>
     *
     * @param needParent
     * @param psiCodeBlock   :
     * @param callingElement :
     * @return com.franklin.java.completion.map.MapDeclaration
     * @author Franklin
     * @since 2021/8/10 16:18
     */
    private MapDeclaration findMapDeclaration(boolean needParent, PsiCodeBlock psiCodeBlock, PsiElement callingElement) throws Exception {
        if (Objects.isNull(psiCodeBlock)) {
            return null;
        }

        PsiMethodCallExpression psiMethodCallExpression = PsiTreeUtil.getParentOfType(callingElement, PsiMethodCallExpression.class);
        MapDeclaration mapDeclaration = findMapDeclarationInMethodChain(psiMethodCallExpression);
        if (Objects.nonNull(mapDeclaration)) {
            return mapDeclaration;
        }

        //foreach循环
        PsiForeachStatement psiForeachStatement = PsiTreeUtil.getParentOfType(callingElement, PsiForeachStatement.class);
        MapDeclaration foreachDeclaration = findMapDeclarationInForeach(callingElement, psiForeachStatement);
        if (Objects.nonNull(foreachDeclaration)) {
            return foreachDeclaration;
        }

        //fori循环
        PsiForStatement psiForStatement = PsiTreeUtil.getParentOfType(callingElement, PsiForStatement.class);
        MapDeclaration foriDeclaration = findMapDeclarationInForI(callingElement, psiForStatement);
        if (Objects.nonNull(foriDeclaration)) {
            return foriDeclaration;
        }

        //lambda表达式
        PsiLambdaExpression psiLambdaExpression = PsiTreeUtil.getParentOfType(callingElement, PsiLambdaExpression.class);
        MapDeclaration lambdaDeclaration = findMapDeclarationInLambda(callingElement, psiLambdaExpression);
        if (Objects.nonNull(lambdaDeclaration)) {
            return lambdaDeclaration;
        }

        String callingElementText = callingElement.getText();
        MapDeclaration parentMap = findMapDeclarationCore(needParent, psiCodeBlock, callingElement, callingElementText);
        if (parentMap != null) return parentMap;
        PsiMethod psiMethod = PsiTreeUtil.getParentOfType(psiCodeBlock, PsiMethod.class);
        PsiClass psiClass = PsiTreeUtil.getParentOfType(psiCodeBlock, PsiClass.class);
        if (Objects.isNull(psiMethod)) {
            //代码块不在Method内
            MapDeclaration declaration = getMapDeclarationInClass(psiClass, callingElementText);
            if (Objects.nonNull(declaration)) {
                return declaration;
            }
        }
        if (psiCodeBlock.getParent() instanceof PsiMethod) {
            //代码块父亲是Method，那就从类里找
            MapDeclaration declaration = getMapDeclarationInClass(psiClass, callingElementText);
            if (Objects.nonNull(declaration)) {
                return declaration;
            }
        }
        //没找到往父代码块找
        PsiCodeBlock parentBlock = PsiTreeUtil.getParentOfType(psiCodeBlock, PsiCodeBlock.class);
        return findMapDeclaration(true, parentBlock, callingElement);
    }

    private MapDeclaration findMapDeclarationInLambda(PsiElement callingElement, PsiLambdaExpression psiLambdaExpression) throws Exception {
        if (Objects.isNull(psiLambdaExpression)) {
            return null;
        }
        PsiParameterList parameterList = psiLambdaExpression.getParameterList();
        PsiParameter[] parameters = parameterList.getParameters();
        for (PsiParameter parameter : parameters) {
            if (parameter.getName().equals(callingElement.getText())) {
                String presentableText = parameter.getType().getPresentableText();
                String typeFullName = PsiUtils.findTypeFullName((PsiJavaFile) callingElement.getContainingFile(), presentableText);
                if (PsiUtils.isJavaMap(callingElement.getProject(), typeFullName)) {
                    PsiMethodCallExpression psiMethodCallExpression = PsiTreeUtil.getParentOfType(psiLambdaExpression, PsiMethodCallExpression.class);
                    if (Objects.isNull(psiMethodCallExpression)) {
                        continue;
                    }
                    PsiElement collectionElement = psiMethodCallExpression.findElementAt(0);
                    PsiData psiData = PsiDataResolverFactory.resolveData(collectionElement.getText(), collectionElement.getText(), collectionElement);
                    PsiElement listDeclareStatement = psiData.getPsiElement();
                    PsiElement elementAt = listDeclareStatement.findElementAt(listDeclareStatement.getText().indexOf(psiData.getValue()));
                    PsiMethodCallExpression fullMethodCallExpression = PsiUtils.findFullMethodCallExpression(elementAt, psiData.getValue());
                    PsiElement psiElement = Objects.isNull(fullMethodCallExpression) ? collectionElement : fullMethodCallExpression;
                    MapDeclaration mapDeclaration = new MapDeclaration(psiElement, "String", "Object", psiData.getValue());
                    mapDeclaration.setCollection();
                    PsiCodeBlock psiCodeBlock = PsiTreeUtil.getParentOfType(psiElement, PsiCodeBlock.class);
                    MapDeclaration parentMap = findParentMap(mapDeclaration, psiCodeBlock, callingElement, psiData.getValue(), "String", "Object");
                    if (Objects.nonNull(parentMap)) {
                        parentMap.setCollection();
                        return parentMap;
                    }
                    return mapDeclaration;
                }
            }
        }
        return null;
    }

    private MapDeclaration findMapDeclarationInForI(PsiElement callingElement, PsiForStatement psiForStatement) throws Exception {
        //todo 暂时不考虑Fori循环
        if (Objects.isNull(psiForStatement)){
            return null;
        }
        List<PsiCodeBlock> codeBlockList = PsiTreeUtil.getChildrenOfTypeAsList(psiForStatement, PsiCodeBlock.class);
        String callingElementText = callingElement.getText();
        String regex = String.format(LIST_MAP_GET_PATTERN_BASE, callingElementText);
        for (PsiCodeBlock psiCodeBlock : codeBlockList) {
            MapDeclaration mapDeclaration = findMapDeclarationCore(true, psiCodeBlock, callingElement, callingElementText);
            if (Objects.nonNull(mapDeclaration)){
                mapDeclaration.setCollection();
                PsiElement declareStatement = mapDeclaration.getDeclareStatement();
                String declareStatementText = declareStatement.getText();
                declareStatementText = StringUtils.deleteWhitespace(declareStatementText);
//                PatternUtils.find(regex)
            }
        }

        return null;
    }

    @Nullable
    private MapDeclaration findMapDeclarationCore(boolean needParent, PsiCodeBlock psiCodeBlock, PsiElement callingElement, String callingElementText) throws Exception {
        String regex = String.format(MAP_TYPE_PATTERN, callingElementText);
        String regex2 = String.format(MAP_TYPE_PATTERN_NO_DECLARE, callingElementText);
        PsiDeclarationStatement[] psiDeclarationStatements = PsiTreeUtil.getChildrenOfType(psiCodeBlock, PsiDeclarationStatement.class);

        if (!ArrayUtils.isEmpty(psiDeclarationStatements)) {
            for (PsiDeclarationStatement psiDeclarationStatement : psiDeclarationStatements) {
                String declarationStatementText = psiDeclarationStatement.getText();
                Map<String, String> findResult = PatternUtils.find(regex, declarationStatementText, "fullStatement", "keyType", "valueType", "declareStatement");
                String fullStatement = findResult.get("fullStatement");
                String keyType = findResult.get("keyType");
                String valeType = findResult.get("valueType");
                String declareStatement = findResult.get("declareStatement");
                if (DataCheckUtils.isNotEmpty(fullStatement)) {

                    MapDeclaration declaration = new MapDeclaration(psiDeclarationStatement, keyType, valeType, declareStatement);
                    if (needParent) {
                        MapDeclaration parentMap = findParentMap(declaration, psiCodeBlock, callingElement, declareStatement, keyType, valeType);
                        if (Objects.nonNull(parentMap)) {

                            return parentMap;
                        }
                    }

                    //匹配成功 ，说明变量是在这里声明的，寻找里面所有put，putAll方法
                    return declaration;
                }

                findResult = PatternUtils.find(regex2, declarationStatementText, "fullStatement", "keyType", "valueType");
                fullStatement = findResult.get("fullStatement");
                keyType = findResult.get("keyType");
                valeType = findResult.get("valueType");
                if (DataCheckUtils.isNotEmpty(fullStatement)) {
                    return new MapDeclaration(psiDeclarationStatement, keyType, valeType);
                }
            }
        }
        return null;
    }

    private MapDeclaration findMapDeclarationInForeach(PsiElement callingElement, PsiForeachStatement psiForeachStatement) throws Exception {
        if (Objects.isNull(psiForeachStatement)) {
            return null;
        }
        PsiParameter iterationParameter = psiForeachStatement.getIterationParameter();
        String typeText = iterationParameter.getType().getPresentableText();
        if (!PsiUtils.isJavaMap(iterationParameter.getProject(), PsiUtils.findTypeFullName((PsiJavaFile) callingElement.getContainingFile(), typeText))) {
            return null;
        }
        if (!callingElement.getText().equals(iterationParameter.getName())) {
            return null;
        }
        PsiExpression collectionElement = psiForeachStatement.getIteratedValue();
        if (Objects.isNull(collectionElement)) {
            return null;
        }
        PsiData psiData = PsiDataResolverFactory.resolveData(collectionElement.getText(), collectionElement.getText(), collectionElement);

        PsiElement listDeclareStatement = psiData.getPsiElement();
        PsiElement elementAt = listDeclareStatement.findElementAt(listDeclareStatement.getText().indexOf(psiData.getValue()));
        PsiMethodCallExpression fullMethodCallExpression = PsiUtils.findFullMethodCallExpression(elementAt, psiData.getValue());
        PsiElement psiElement = Objects.isNull(fullMethodCallExpression) ? collectionElement : fullMethodCallExpression;
        MapDeclaration mapDeclaration = new MapDeclaration(psiElement, "String", "Object", psiData.getValue());
        mapDeclaration.setCollection();
        return mapDeclaration;
    }

    private MapDeclaration findParentMap(
        MapDeclaration childDeclaration,
        PsiCodeBlock psiCodeBlock,
        PsiElement callingElement,
        String declareStatement,
        String keyType,
        String valueType
    ) throws Exception {
        if (Objects.isNull(psiCodeBlock)) {
            return null;
        }
        String psiCodeBlockText = psiCodeBlock.getText();
        String regex = String.format(MAP_FROM_MAP_PATTERN_BASE, keyType, valueType);
        Map<String, String> parentResult = PatternUtils.find(regex, declareStatement, "parentMap", "key");
        String parentMap = parentResult.get("parentMap");
        String key = parentResult.get("key");
        if (DataCheckUtils.isEmpty(parentMap)) {
            return null;
        }
        String[] lines = psiCodeBlockText.split("\n");
        String mapPutRegex = String.format(MAP_PUT_KEY_PATTERN, parentMap, key);
        String parentDeclareRegex = String.format(MAP_TYPE_PATTERN, parentMap);
        int callingElementStartOffset = PsiUtils.findStartOffsetInPsiFile(callingElement);
        int length = 0;
        PsiElement valueElement = null;
        String value = null;

        String parentKeyType = "String";
        String parentValueType = "Object";
        String parentDeclareStatement = null;

        PsiElement parentElement = null;

        for (String line : lines) {
            if (line.trim().startsWith("/")) {
                length = length + line.length() + 1;
                continue;
            }
            PsiElement element = psiCodeBlock.findElementAt(length + line.length() / 2);
            int offset = PsiUtils.findStartOffsetInPsiFile(element);
            if (offset > callingElementStartOffset) {
                break;
            }

            Map<String, String> findResult = PatternUtils.find(mapPutRegex, line, "value");
            String valueResult = findResult.get("value");
            if (DataCheckUtils.isNotEmpty(valueResult)) {
                int index = length + line.indexOf(valueResult, line.indexOf(key) + key.length());
                if (index >= 0) {
                    value = valueResult;
                    valueElement = psiCodeBlock.findElementAt(index);
                }
            }

            String declarationStatementText = line;
            Map<String, String> parentDeclareResult = PatternUtils.find(parentDeclareRegex, declarationStatementText, "fullStatement", "keyType", "valueType", "declareStatement");
            String fullStatement = parentDeclareResult.get("fullStatement");
            String pKeyType = parentDeclareResult.get("keyType");
            String pValeType = parentDeclareResult.get("valueType");
            String pDeclareStatement = parentDeclareResult.get("declareStatement");

            if (DataCheckUtils.isNotEmpty(fullStatement)) {
                parentKeyType = pKeyType;
                parentValueType = pValeType;
                parentDeclareStatement = pDeclareStatement;
                parentElement = psiCodeBlock.findElementAt(length + line.indexOf(parentMap));
            }

            length = length + line.length() + 1;
        }

        childDeclaration.setAssignKey(key);
        childDeclaration.setAssignValue(value);
        MapDeclaration finalParentDeclaration = null;
        if (DataCheckUtils.isNotEmpty(parentDeclareStatement)) {
            MapDeclaration parentDeclaration = new MapDeclaration(parentElement, parentKeyType, parentValueType, parentDeclareStatement);

            parentDeclaration.setAssignKey(key);
            parentDeclaration.setAssignValue(value);
            parentDeclaration.setChild(childDeclaration);
            parentDeclaration.setParent(true);
            finalParentDeclaration = findParentMap(parentDeclaration, psiCodeBlock, parentElement, parentDeclareStatement, parentKeyType, parentValueType);
            if (Objects.nonNull(finalParentDeclaration)) {
                finalParentDeclaration.setParent(true);
                finalParentDeclaration.setAssignKey(key);
                finalParentDeclaration.setAssignValue(value);
                return finalParentDeclaration;
            }
        }

        if (Objects.nonNull(valueElement)) {
            if (value.equals(valueElement.getText())) {
                //先判断是不是本地变量
                MapDeclaration parentDeclaration = findMapDeclaration(true, psiCodeBlock, valueElement);
                if (Objects.nonNull(parentDeclaration)) {
                    parentDeclaration.setParent(true);
                    parentDeclaration.setChild(childDeclaration);
                    parentDeclaration.setAssignKey(key);
                    parentDeclaration.setAssignValue(value);
                    return parentDeclaration;
                }
            } else {
                PsiMethodCallExpression fullMethodCallExpression = PsiUtils.findFullMethodCallExpression(valueElement, value);
                MapDeclaration mapDeclarationInMethodChain = findMapDeclarationInMethodChain(fullMethodCallExpression);
                if (Objects.nonNull(mapDeclarationInMethodChain)) {
                    mapDeclarationInMethodChain.setChild(childDeclaration);
                    mapDeclarationInMethodChain.setParent(true);
                    mapDeclarationInMethodChain.setAssignKey(key);
                    mapDeclarationInMethodChain.setAssignValue(value);
                    return mapDeclarationInMethodChain;
                }
            }
        }

        if (Objects.nonNull(parentElement)) {
            MapDeclaration parentDeclaration = findMapDeclaration(true, psiCodeBlock, parentElement);

            if (Objects.nonNull(parentDeclaration)) {
                parentDeclaration.setChild(childDeclaration);
                parentDeclaration.setParent(true);
                parentDeclaration.setAssignKey(key);
                parentDeclaration.setAssignValue(value);
                return parentDeclaration;
            }
        }

        PsiCodeBlock parentBlock = PsiTreeUtil.getParentOfType(psiCodeBlock, PsiCodeBlock.class);
        MapDeclaration parentMap1 = findParentMap(childDeclaration, parentBlock, callingElement, declareStatement, keyType, valueType);
        if (Objects.nonNull(parentMap1)) {
            parentMap1.setChild(childDeclaration);
            parentMap1.setParent(true);
            parentMap1.setAssignKey(key);
            parentMap1.setAssignValue(value);
        }
        return parentMap1;
    }

    @Nullable
    private MapDeclaration findMapDeclarationInMethodChain(PsiMethodCallExpression psiMethodCallExpression) {
        if (Objects.isNull(psiMethodCallExpression)) {
            return null;
        }
        PsiMethod psiMethod = psiMethodCallExpression.resolveMethod();
        if (Objects.nonNull(psiMethod)) {
            PsiType returnType = psiMethod.getReturnType();
            if (Objects.nonNull(returnType)) {
                String declarationStatementText = returnType.getPresentableText();
                String qualifiedName = PsiUtils.findTypeFullName((PsiJavaFile) psiMethod.getContainingFile(), declarationStatementText);
                if (PsiUtils.isJavaMap(psiMethod.getProject(), qualifiedName)) {
                    String regex = String.format(MAP_TYPE_PATTERN_NO_DECLARE, "");
                    Map<String, String> findResult = PatternUtils.find(regex, declarationStatementText, "fullStatement", "keyType", "valueType");
                    String fullStatement = findResult.get("fullStatement");
                    String keyType = findResult.get("keyType");
                    String valeType = findResult.get("valueType");
                    if (DataCheckUtils.isNotEmpty(fullStatement)) {
                        return new MapDeclaration(psiMethodCallExpression, keyType, valeType);
                    }
                } else if (PsiUtils.isListMap(declarationStatementText)) {
                    return new MapDeclaration(psiMethodCallExpression, "String", "Object");
                }
            }
        }
        //链式调用
        psiMethodCallExpression = PsiTreeUtil.getParentOfType(psiMethodCallExpression, PsiMethodCallExpression.class);
        return findMapDeclarationInMethodChain(psiMethodCallExpression);
    }

    @Nullable
    private MapDeclaration getMapDeclarationInClass(PsiClass psiClass, String callingElementText) {
        if (Objects.nonNull(psiClass)) {
            String regex = String.format(MAP_TYPE_PATTERN, callingElementText);
            String regex2 = String.format(MAP_TYPE_PATTERN_NO_DECLARE, callingElementText);
            PsiField[] allFields = psiClass.getAllFields();
            for (PsiField field : allFields) {
                if (field.getName().equals(callingElementText)) {
                    Map<String, String> findResult = PatternUtils.find(regex, field.getText(), "fullStatement", "keyType", "valueType", "declareStatement");
                    String fullStatement = findResult.get("fullStatement");
                    String keyType = findResult.get("keyType");
                    String valeType = findResult.get("valueType");
                    String declareStatement = findResult.get("declareStatement");
                    if (DataCheckUtils.isNotEmpty(fullStatement)) {
                        return new MapDeclaration(field, keyType, valeType, declareStatement);
                    }

                    findResult = PatternUtils.find(regex2, field.getText(), "fullStatement", "keyType", "valueType");
                    fullStatement = findResult.get("fullStatement");
                    keyType = findResult.get("keyType");
                    valeType = findResult.get("valueType");
                    if (DataCheckUtils.isNotEmpty(fullStatement)) {
                        return new MapDeclaration(field, keyType, valeType);
                    }
                }
            }
        }
        return null;
    }

    private List<PsiParameter> findIterationParameter(PsiElement collectionElement, PsiCodeBlock psiCodeBlock) {
        String[] lines = psiCodeBlock.getText().split("\n");
        int length = 0;
        List<PsiParameter> paramList = new PrettyArrayList<>();
        Set<PsiForeachStatement> foreachStatementSet = new PrettyHashSet<>();
        String collectionElementText = collectionElement.getText();
        for (String line : lines) {
            if (line.trim().startsWith("/")) {
                length = length + line.length() + 1;
                continue;
            }
            PsiElement psiElement = psiCodeBlock.findElementAt(length + line.length() / 2);
            if (Objects.nonNull(psiElement)) {
                boolean foundParam = Optional.ofNullable(PsiTreeUtil.getParentOfType(psiElement, PsiForeachStatement.class))
                    .filter(psiForeachStatement -> {
                        PsiExpression iteratedValue = psiForeachStatement.getIteratedValue();
                        if (Objects.isNull(iteratedValue)) {
                            return false;
                        }
                        return iteratedValue.getText().equals(collectionElementText);
                    })
                    .filter(psiForeachStatement -> {
                        if (foreachStatementSet.contains(psiForeachStatement)) {
                            return false;
                        }
                        return foreachStatementSet.add(psiForeachStatement);
                    })
                    .map(PsiForeachStatement::getIterationParameter)
                    .filter(psiParameter -> PsiUtils.findStartOffsetInPsiFile(psiParameter) < PsiUtils.findStartOffsetInPsiFile(collectionElement))
                    .map(paramList::add)
                    .orElse(false);

                AtomicReference<PsiMethodCallExpression> reference = new AtomicReference<>();
                Optional.ofNullable(PsiTreeUtil.getParentOfType(psiElement, PsiLambdaExpression.class))
                    .filter(psiLambdaExpression -> {
                        PsiMethodCallExpression psiMethodCallExpression = PsiUtils.findFullMethodCallExpression(psiLambdaExpression);
                        if (Objects.isNull(psiMethodCallExpression)) {
                            return false;
                        }
                        reference.set(psiMethodCallExpression);
                        String lambdaExpressionText = StringUtils.deleteWhitespace(psiMethodCallExpression.getText());
                        String[] chains = lambdaExpressionText.split("\\.");
                        if (chains.length < 2) {
                            return false;
                        }
                        PsiMethodCallExpression fullMethodCallExpression = PsiUtils.findFullMethodCallExpression(collectionElement);
                        if (Objects.nonNull(fullMethodCallExpression) && fullMethodCallExpression.equals(psiMethodCallExpression)){
                            return true;
                        }
                        return chains[0].equals(collectionElementText) || chains[1].equals(collectionElementText);
                    })
                    .map(psiLambdaExpression -> reference.get())
                    .map(psiMethodCallExpression -> {
                        List<PsiLambdaExpression> lambdaExpressionList = new PrettyArrayList<>();
                        String lambdaExpressionText = psiMethodCallExpression.getText();
                        int index = 0;
                        int fromIndex = 0;
                        while ((index = lambdaExpressionText.indexOf("->", fromIndex)) >= 0) {
                            PsiLambdaExpression psiLambdaExpression = Optional.ofNullable(psiMethodCallExpression.findElementAt(index))
                                .map(arrow -> PsiTreeUtil.getParentOfType(arrow, PsiLambdaExpression.class))
                                .orElse(null);
                            if (Objects.nonNull(psiLambdaExpression)) {
                                lambdaExpressionList.add(psiLambdaExpression);
                                fromIndex = index + psiLambdaExpression.getText().length();
                            }
                        }
                        return lambdaExpressionList;
                    })
                    .filter(DataCheckUtils::isNotEmpty)
                    .ifPresent(psiLambdaExpressions -> {
                        psiLambdaExpressions.forEach(psiLambdaExpression -> {
                            if (PsiUtils.findStartOffsetInPsiFile(psiLambdaExpression) < PsiUtils.findStartOffsetInPsiFile(collectionElement)) {
                                PsiParameter[] parameters = psiLambdaExpression.getParameterList().getParameters();
                                for (PsiParameter parameter : parameters) {
                                    String typeText = parameter.getType().getCanonicalText();
                                    if (PsiUtils.isJavaMap(parameter.getProject(), typeText)) {
                                        paramList.add(parameter);
                                        break;
                                    }
                                }
                            }
                        });
                    });

            }

            length = length + line.length() + 1;
        }
        return paramList;
    }

    /**
     * 寻找put方法的值<br>
     *
     * @param depth
     * @param psiCodeBlock   :
     * @param callingElement :
     * @return java.util.List<com.franklin.java.completion.map.MapData>
     * @author Franklin
     * @since 2021/8/6 17:52
     */
    private void findPutMethod(
        int depth,
        @NotNull PsiCodeBlock psiCodeBlock,
        @NotNull PsiElement callingElement,
        List<MapData> mapDataList
    ) throws Exception {
        if (depth > basicDepth.get()) {
            return;
        }
        List<MapDeclaration> mapDeclarations = basicDeclarations.get();
        MapDeclaration mapDeclaration = mapDeclarations.get(depth - 1);
        List<String> paramList = new PrettyArrayList<>();
        if (mapDeclaration.isCollection()) {
            List<PsiParameter> parameterList = findIterationParameter(callingElement, psiCodeBlock);
            parameterList.forEach(psiParameter -> paramList.add(psiParameter.getName()));
        }else {
            paramList.add(callingElement.getText());
        }
        for (String callingElementText : paramList) {
            String mapPutRegex = String.format(MAP_PUT_PATTERN, callingElementText);
            String mapPutAllRegex = String.format(MAP_PUT_ALL_PATTERN, callingElementText);
            int callingElementOffset = PsiUtils.findStartOffsetInPsiFile(callingElement);
            String blockText = psiCodeBlock.getText();
            String[] lines = blockText.split("\n");
            int length = 0;
            for (String line : lines) {
                if (line.trim().startsWith("/")) {
                    length = length + line.length() + 1;
                    continue;
                }
                String expressionText = line;
                PsiElement element = psiCodeBlock.findElementAt(length + expressionText.length() / 2);
                int elementOffset = PsiUtils.findStartOffsetInPsiFile(element);
                if (elementOffset > callingElementOffset) {
                    break;
                }
                Map<String, String> findResult = PatternUtils.find(mapPutRegex, expressionText, "key", "value");
                if (DataCheckUtils.isNotEmpty(findResult)) {
                    //put() putIfAbsent()
                    String key = findResult.get("key");
                    String value = findResult.get("value");
                    int keyIndex = expressionText.indexOf(key);
                    PsiElement keyElement = psiCodeBlock.findElementAt(length + keyIndex);
                    PsiElement valueElement = psiCodeBlock.findElementAt(length + expressionText.indexOf(value, keyIndex + key.length()));
                    PsiData keyData = PsiDataResolverFactory.resolveData(key, key, keyElement);
                    PsiData valueData = PsiDataResolverFactory.resolveData(value, value, valueElement);
                    MapDeclaration mapDeclarationChild = mapDeclaration.getChild();
                    if (
                        Objects.nonNull(mapDeclarationChild) &&
                            StringHelper.equals(mapDeclarationChild.getAssignKey(), key)
                    ) {
                        PsiElement mapElement = valueData.getPsiElement();
                        if (mapElement instanceof PsiMethodCallExpression) {
                            PsiMethodCallExpression psiMethodCallExpression = (PsiMethodCallExpression) mapElement;
                            PsiCodeBlock psiMethodBody = psiMethodCallExpression.resolveMethod().getBody();
                            findMapData(depth + 1, psiMethodBody, mapElement, findMapDeclarationInMethodChain(psiMethodCallExpression), mapDataList);
                        } else {
                            PsiCodeBlock body = PsiTreeUtil.getParentOfType(mapElement, PsiCodeBlock.class);
                            findMapData(depth + 1, body, valueElement, null, mapDataList);
                        }
                    }
                    if (depth == basicDepth.get()) {
                        mapDataList.add(new MapData(keyData, valueData, callingElement));
                    }
                } else {
                    //putAll()
                    findResult = PatternUtils.find(mapPutAllRegex, expressionText, "originMap");
                    String originMap = findResult.getOrDefault("originMap", "");
                    if (DataCheckUtils.isNotEmpty(originMap)) {
                        PsiElement originMapElement = psiCodeBlock.findElementAt(length + expressionText.indexOf(originMap));
                        PsiMethodCallExpression fullMethodCallExpression = PsiUtils.findFullMethodCallExpression(originMapElement, originMap);
//                    originMapElement = Objects.isNull(fullMethodCallExpression) ? originMapElement : fullMethodCallExpression;
                        MapDeclaration originMapDeclaration = findMapDeclaration(true, psiCodeBlock, originMapElement);
                        if (Objects.nonNull(originMapElement)) {
                            //先从当前方法里找
                            PsiMethod psiMethod = PsiTreeUtil.getParentOfType(callingElement, PsiMethod.class);
                            PsiCodeBlock psiMethodBody = null;
                            if (Objects.nonNull(psiMethod)) {
                                //找子代码块
                                psiMethodBody = psiMethod.getBody();
                            }
                            findMapData(depth, psiMethodBody, originMapElement, originMapDeclaration, mapDataList);
                        }
                    }
                }
                length = length + line.length() + 1;
            }
        }
    }

}
